﻿using System.Runtime.CompilerServices;
using CuteAnt.AsyncEx;

namespace System.Threading.Tasks
{
  /// <summary>Provides extension methods for task factories.</summary>
  internal static class TaskFactoryExtensions
  {
    /// <summary>Queues work to the task factory and returns a <see cref="Task"/> representing that work. 
    /// If the task factory does not specify a task scheduler, the thread pool task scheduler is used.</summary>
    /// <param name="taskFactory">The <see cref="TaskFactory"/>. May not be <c>null</c>.</param>
    /// <param name="action">The action delegate to execute. May not be <c>null</c>.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
    /// <returns>The started task.</returns>
    internal static Task Run(this TaskFactory taskFactory, Action action, CancellationToken cancellationToken = default)
    {
      if (taskFactory == null) ThrowArgumentNullException_TaskFactory();
      if (action == null) ThrowArgumentNullException_Action();

      return taskFactory.StartNew(action, cancellationToken,
          AsyncUtils.GetCreationOptions(taskFactory.CreationOptions), taskFactory.Scheduler ?? TaskScheduler.Default);
    }

    /// <summary>Queues work to the task factory and returns a <see cref="Task{TResult}"/> representing that work. 
    /// If the task factory does not specify a task scheduler, the thread pool task scheduler is used.</summary>
    /// <param name="taskFactory">The <see cref="TaskFactory"/>. May not be <c>null</c>.</param>
    /// <param name="action">The action delegate to execute. May not be <c>null</c>.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
    /// <returns>The started task.</returns>
    internal static Task<TResult> Run<TResult>(this TaskFactory taskFactory, Func<TResult> action, CancellationToken cancellationToken = default)
    {
      if (taskFactory == null) ThrowArgumentNullException_TaskFactory();
      if (action == null) ThrowArgumentNullException_Action();

      return taskFactory.StartNew(action, cancellationToken,
          AsyncUtils.GetCreationOptions(taskFactory.CreationOptions), taskFactory.Scheduler ?? TaskScheduler.Default);
    }

    /// <summary>Queues work to the task factory and returns a proxy <see cref="Task"/> representing that work. 
    /// If the task factory does not specify a task scheduler, the thread pool task scheduler is used.</summary>
    /// <param name="taskFactory">The <see cref="TaskFactory"/>. May not be <c>null</c>.</param>
    /// <param name="action">The action delegate to execute. May not be <c>null</c>.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
    /// <returns>The started task.</returns>
    internal static Task Run(this TaskFactory taskFactory, Func<Task> action, CancellationToken cancellationToken = default)
    {
      if (taskFactory == null) ThrowArgumentNullException_TaskFactory();
      if (action == null) ThrowArgumentNullException_Action();

      return taskFactory.StartNew(action, cancellationToken,
          AsyncUtils.GetCreationOptions(taskFactory.CreationOptions), taskFactory.Scheduler ?? TaskScheduler.Default).Unwrap();
    }

    /// <summary>Queues work to the task factory and returns a proxy <see cref="Task{TResult}"/> representing that work. 
    /// If the task factory does not specify a task scheduler, the thread pool task scheduler is used.</summary>
    /// <param name="taskFactory">The <see cref="TaskFactory"/>. May not be <c>null</c>.</param>
    /// <param name="action">The action delegate to execute. May not be <c>null</c>.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
    /// <returns>The started task.</returns>
    internal static Task<TResult> Run<TResult>(this TaskFactory taskFactory, Func<Task<TResult>> action, CancellationToken cancellationToken = default)
    {
      if (taskFactory == null) ThrowArgumentNullException_TaskFactory();
      if (action == null) ThrowArgumentNullException_Action();

      return taskFactory.StartNew(action, cancellationToken,
          AsyncUtils.GetCreationOptions(taskFactory.CreationOptions), taskFactory.Scheduler ?? TaskScheduler.Default).Unwrap();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void ThrowArgumentNullException_Action()
    {
      throw GetArgumentNullException();
      ArgumentNullException GetArgumentNullException()
      {
        return new ArgumentNullException("action");
      }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void ThrowArgumentNullException_TaskFactory()
    {
      throw GetArgumentNullException();
      ArgumentNullException GetArgumentNullException()
      {
        return new ArgumentNullException("taskFactory");
      }
    }
  }
}